//  This file is part of Swordbible - http://www.swordwarrior.net
//  Copyright (C) 2003-2009 Manu Mital mital.manu@gmail.com

//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2, or (at your option)
//  any later version.

//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.

//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software Foundation,
//  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


// BottomLeft.cpp : implementation file
//

#include <stdafx.h>
#include "SwordBible.h"
#include "SwordBibleDoc.h"
#include "BottomLeft.h"
#include ".\bottomleft.h"
#include "ColorMgr.h"
#include "BibleForm.h"



CString CBottomLeft::ActiveMainFrameCommand;
CSwordBibleFonts CBottomLeft::font;

int CBottomLeft::active = -1;
// CBottomLeft dialog


IMPLEMENT_DYNAMIC(CBottomLeft, CPropertyPage)
CBottomLeft::CBottomLeft()
	: CPropertyPage(CBottomLeft::IDD)
{
	
	pModule = NULL;
	
}

CBottomLeft::~CBottomLeft()
{
	

}

void CBottomLeft::DoDataExchange(CDataExchange* pDX)
{
	CPropertyPage::DoDataExchange(pDX);

	DDX_Control(pDX, IDC_RICHEDIT_BOTTOMLEFT, m_lxRich);
	DDX_Control(pDX, IDC_LIST_ENTRIES, EntriesList);
	DDX_Control(pDX, IDC_SEARCH_DICT, SearchEdit);
	
}


BEGIN_MESSAGE_MAP(CBottomLeft, CPropertyPage)
	ON_WM_SIZE()
	ON_COMMAND(ID_BOTTOMLEFT_SELECTALL, OnSelectAll)
	ON_COMMAND(ID_BOTTOMLEFT_COPY, OnBottomleftCopy)
	ON_COMMAND(ID_BOTTOMLEFT_FONT, OnBottomleftFont)
	ON_COMMAND(ID_BOTTOMLEFT_PRINT,OnPrint)
	
	ON_COMMAND(ID_BOTTOMLEFT_ZOOM, OnZoom)
	ON_UPDATE_COMMAND_UI(ID_BOTTOMLEFT_ZOOM, OnUpdateZoom)

	ON_COMMAND(ID_BOTTOMLEFT_NEXT,OnNext)
	ON_COMMAND(ID_BOTTOMLEFT_PREVIOUS,OnPrevious)
  ON_COMMAND(ID_DICTIONARY_INFORMATION, OnInfo)

	ON_COMMAND(ID_DICTIONARY_VERSIONS, OnShowVersions)	


	ON_MESSAGE(ID_DICTIONARY_COMMAND, OnMainFrameCommand)

   ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
   ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
   ON_NOTIFY(LVN_GETDISPINFO, IDC_LIST_ENTRIES, &CBottomLeft::GetDispInfo)
   ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_ENTRIES, &CBottomLeft::OnItemChanged)
 
  ON_NOTIFY(EN_MSGFILTER, IDC_RICHEDIT_BOTTOMLEFT, &CBottomLeft::OnEnMsgfilter)
  ON_EN_SETFOCUS(IDC_SEARCH_DICT, &CBottomLeft::OnFocusSearchEditBox)
END_MESSAGE_MAP()


// CBottomLeft message handlers

void CBottomLeft::OnSize(UINT nType, int cx, int cy)
{
	CPropertyPage::OnSize(nType, cx, cy);

	CWnd* pWnd = GetDlgItem(IDC_RICHEDIT_BOTTOMLEFT);

	// resize the rich edit control
	if( ::IsWindow(pWnd ->GetSafeHwnd() )) 
	pWnd ->SetWindowPos(NULL,10,40,cx-200,cy-50,SWP_NOZORDER | SWP_NOACTIVATE);

	// resize the list box
	if( ::IsWindow( EntriesList.GetSafeHwnd() ))
		EntriesList.SetWindowPos(NULL, cx - 180, 70, 170 , cy - 80,  SWP_NOZORDER | SWP_NOACTIVATE);

	// resize the edit box
	if( ::IsWindow( SearchEdit.GetSafeHwnd() ))
		SearchEdit.SetWindowPos(NULL, cx - 180, 40, 170 , 24,  SWP_NOZORDER | SWP_NOACTIVATE);

	



}

BOOL CBottomLeft::OnInitDialog()
{
	CPropertyPage::OnInitDialog();

	if(CSwordBibleDoc::numDictAvailable > 0) 
	{
		active = 0;
		pModule = (sword::SWLD*)CSwordBibleDoc::pRTFDictModule[active];
	}
	

	// to receive the mouse click events
	m_lxRich.SetEventMask(ENM_MOUSEEVENTS | ENM_KEYEVENTS | ENM_SCROLLEVENTS);
	m_lxRich.SendMessage(EM_EXLIMITTEXT,0,-1);

	// load the font for displaying the bottom left modules
	font.location = AFX_BOTTOMLEFT;
	font.Load();
	font.ConstructRTF();
	m_lxRich.SetBackgroundColor(FALSE, CColorMgr::RTFtoCOLORREF(font.bg_color) );

  EnableToolTips(TRUE);   // enable tool tips for view

  LoadToolbar();


	// Insert a column in the list control
    EntriesList.InsertColumn(0,_T("Dictionary") );
	
	// set the width of the column
	CRect listRect;
	EntriesList.GetWindowRect(listRect);
	int width = listRect.Width();
	EntriesList.SetColumnWidth(0, width);

	// Set the item count for the list.If you set the item count to 100 or 1,000,000 doesn't matter, 
	//the time to run this command will still be practically zero. In a non-virtual list, 
	// adding a million elements could take hours.
	numEntries = GetNumEntries();
	EntriesList.SetItemCount(numEntries);

  last_pos = 0;
  if(pModule) {
  pModule->setPosition(sword::TOP);
  pModule->increment(1);
  }

	LONG32 style = EntriesList.GetExtendedStyle();
	style |= LVS_EX_FULLROWSELECT;
	EntriesList.SetExtendedStyle(style);

  SearchEdit.SetWindowText(_T("Type text & hit Enter to search"));

	EntriesList.EnableScrollBar(SB_BOTH, ESB_DISABLE_BOTH);


	
	return FALSE;  // return TRUE unless you set the focus to a control
	// EXCEPTION: OCX Property Pages should return FALSE
}

BOOL CBottomLeft::OnSetActive()
{

  if(pModule == NULL) return CPropertyPage::OnSetActive();

  last_pos = 0;
  pModule->setPosition(sword::TOP);
  pModule->increment(1);

   SearchEdit.SetWindowText(_T("Type text & hit Enter to search"));
 
   EntriesList.SetItemState(1,LVIS_SELECTED | LVIS_FOCUSED,LVIS_SELECTED | LVIS_FOCUSED);
   EntriesList.SetItemState(0,LVIS_SELECTED | LVIS_FOCUSED,LVIS_SELECTED | LVIS_FOCUSED);

   	EntriesList.EnsureVisible(0,FALSE); 
		
  return CPropertyPage::OnSetActive();

}


void CBottomLeft::LoadToolbar()
{

	if (!m_wndFormatBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD
       | WS_VISIBLE | CBRS_TOP |  CBRS_FLYBY | CBRS_TOOLTIPS) ||
       !m_wndFormatBar.LoadToolBar(IDR_BOTTOMLEFT_TOOLBAR))
   {
      TRACE0("Failed to create toolbar\n");
      return;      // fail to create
   }

  m_wndFormatBar.LoadTCToolBar(24, IDB_BITMAP_BOTTOMLEFT,IDB_BITMAP_BOTTOMLEFT,
                              IDB_BITMAP_BOTTOMLEFT, RGB(255,255,255));
	

	// Position the control bars using RepositionBars. 
	RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);

	// Expand the size of the Toolbar window so all controls in it would fit
	
	CRect toolBarRect;
	CToolBarCtrl& formatBar = m_wndFormatBar.GetToolBarCtrl();
	formatBar.GetWindowRect(toolBarRect);
	ScreenToClient(toolBarRect);
	toolBarRect.left += 10  ;
	toolBarRect.right += 500  ;
	formatBar.MoveWindow(toolBarRect,TRUE);


}

int CBottomLeft::GetNumEntries()
{
	if(!pModule) return 0;

	// initial guess
	high = 99500; low =  0; mid = (high + low)/2;  int last = 0;

	pModule->setPosition(sword::TOP); 
  pModule->increment(1);

	while(high - low > 1) {

	pModule->increment(mid - last);
	last = mid;

	if(pModule->Error()) { pModule->setPosition(sword::TOP); pModule->increment(1);
  high = mid; last = 0;}

	else low = mid;	
	
	mid = (high + low)/2; 
	}


 return high;
  
}


void CBottomLeft::OnBottomleftCopy()
{
	if(  !( ((CPropertySheet*) GetParent())->GetActivePage()   == this) )  
	  return;
	
	m_lxRich.Copy();

}

void CBottomLeft::OnSelectAll(void)
{
	// only the active page should handle the message
  if(  !( ((CPropertySheet*) GetParent())->GetActivePage()   == this) )  
	  return;

  m_lxRich.SetFocus();
  m_lxRich.SetSel(0,-1);

}




LONG CBottomLeft::OnMainFrameCommand(UINT, LONG)
{ 
	
	// only the active page should handle the message
	  if(  !( ((CPropertySheet*) GetParent())->GetActivePage()   == this) )  
	  return 0L;

	
	if (ActiveMainFrameCommand == _T("print"))		           OnPrint();
	else if (ActiveMainFrameCommand == _T("copy"))			     OnBottomleftCopy();
	else if (ActiveMainFrameCommand == _T("selectall"))      OnSelectAll();
	else if (ActiveMainFrameCommand == _T("display"))        OnBottomleftFont();
	else if (ActiveMainFrameCommand == _T("zoom"))           OnZoom();
	else if (ActiveMainFrameCommand == _T("previous"))		   OnPrevious();
	else if (ActiveMainFrameCommand == _T("next"))           OnNext();
  else if(ActiveMainFrameCommand == _T("info"))            OnInfo();

	
	return 0L;

}

void CBottomLeft::OnBottomleftFont()
{
	// only the active page should handle the message
  if(     ((CPropertySheet*) GetParent())->GetActivePage()   != this) return;

  CColorMgr colordlg(NULL, &font);
	
  if(colordlg.DoModal() == IDOK){
   m_lxRich.SetBackgroundColor(false, CColorMgr::RTFtoCOLORREF(font.bg_color) );
  }

	
}


BOOL CBottomLeft::OnToolTipNotify(UINT id, NMHDR *pNMHDR,   LRESULT *pResult)
{
   ASSERT(pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW);

     // if there is a top level routing frame then let it handle the message
     if (GetRoutingFrame() != NULL) return FALSE;

     // to be thorough we will need to handle UNICODE versions of the message also !!
     TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
     TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
     TCHAR szFullText[512];
     CString strTipText;
     UINT nID = pNMHDR->idFrom;

     if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
         pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
     {
          // idFrom is actually the HWND of the tool 
          nID = ::GetDlgCtrlID((HWND)nID);
     }

     if (nID != 0) // will be zero on a separator
     {
          AfxLoadString(nID, szFullText);
          strTipText=szFullText;

#ifndef _UNICODE
          if (pNMHDR->code == TTN_NEEDTEXTA)
          {
               lstrcpyn(pTTTA->szText, strTipText, sizeof(pTTTA->szText));
          }
          else
          {
               _mbstowcsz(pTTTW->szText, strTipText, sizeof(pTTTW->szText));
          }
#else
          if (pNMHDR->code == TTN_NEEDTEXTA)
          {
               _wcstombsz(pTTTA->szText, strTipText,sizeof(pTTTA->szText));
          }
          else
          {
               lstrcpyn(pTTTW->szText, strTipText, sizeof(pTTTW->szText));
          }
#endif

          *pResult = 0;

          // bring the tooltip window above other popup windows
          ::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,SWP_NOACTIVATE|
               SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER); return TRUE;
     }
      return FALSE;
}


void CBottomLeft::OnPrint()
{
		// only the active page should handle the message
  if(  !( ((CPropertySheet*) GetParent())->GetActivePage()   == this) )  
	  return;

	CBibleForm::pControl = (CRichEditCtrl *) GetDlgItem(IDC_RICHEDIT_BOTTOMLEFT);
	AfxGetMainWnd()->SendMessageToDescendants(WM_PRINT_CONTROL, (WPARAM) 1 , 0);

}




void CBottomLeft::OnZoom()
{
	// only the active page should handle the message
  if(  !( ((CPropertySheet*) GetParent())->GetActivePage()   == this) )  
	  return;


	CSwordBibleDoc::dictZoom = !(CSwordBibleDoc::dictZoom);

	ZoomText();

}

void CBottomLeft::OnUpdateZoom(CCmdUI *pCmdUI)
{

	// only the active page should handle the message
  if(  !( ((CPropertySheet*) GetParent())->GetActivePage()   == this) )  
	  return;


	pCmdUI->SetCheck(CSwordBibleDoc::dictZoom);
	
}

void CBottomLeft::ZoomText()
{

	if(CSwordBibleDoc::dictZoom) m_lxRich.SendMessage(EM_SETZOOM,5,4); //zoom by 1.25
	else m_lxRich.SendMessage(EM_SETZOOM, 0,0);  // turn off zooming

	
}

void CBottomLeft::OnPrevious()
{
	// select the first item in the list
	POSITION pos = EntriesList.GetFirstSelectedItemPosition( );
	int sel = EntriesList.GetNextSelectedItem(pos);

	if(sel < 0) return;

	EntriesList.SetItemState(sel - 1,LVIS_SELECTED | LVIS_FOCUSED,LVIS_SELECTED | LVIS_FOCUSED);
	EntriesList.EnsureVisible(sel -1 ,FALSE); 

}

void CBottomLeft::OnNext()
{
	// select the first item in the list
	POSITION pos = EntriesList.GetFirstSelectedItemPosition( );
	int sel = EntriesList.GetNextSelectedItem(pos);

	if(sel < 0) return;

	EntriesList.SetItemState(sel + 1,LVIS_SELECTED | LVIS_FOCUSED,LVIS_SELECTED | LVIS_FOCUSED);
	EntriesList.EnsureVisible(sel + 1 ,FALSE); 

}

void CBottomLeft::OnInfo()
{
  if (!pModule) return;   // check that pointer is not NULL



  CString info = font.GetHeaderRTF();
  info +=  pModule->getConfigEntry("About") ;

  info += _T("}");

  m_lxRich.SetRTF(info);

  
}


void CBottomLeft::GetDispInfo(NMHDR *pNMHDR, LRESULT *pResult)
{
	NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);

  if(!pModule) return;

	 //Create a pointer to the item
    LV_ITEM* pItem= &(pDispInfo)->item;

    //Which item number?
    int itemid = pItem->iItem;

    //Do the list need text information?
    if ((pItem->mask & LVIF_TEXT) && pItem->iSubItem == 0)
    {
        //Text is name
	
		pModule->increment(itemid - last_pos);
		last_pos = itemid;
                
        //Copy the text to the LV_ITEM structure
        //Maximum number of characters is in pItem->cchTextMax
        lstrcpyn(pItem->pszText, (CString) pModule->getKeyText(), pItem->cchTextMax);

     }



	*pResult = 0;
}


// finds an entry in the list Ctrl. Returns index of partial/full match
// Binary search
int CBottomLeft::FindEntry(CString& toFind)
{
	if(!pModule) return 0;


	// initial guess
	high = numEntries + 1; low =  0; mid = (high + low)/2;

	while(high - low > 1) {
		
	pModule->increment(mid - last_pos);
  last_pos = mid;

	KEYText  = pModule->getKeyText();
	
	result = KEYText.CompareNoCase(toFind);

	if(result == 0) return mid;
	else if (result > 0)   high = mid;
	else	low = mid;
	
	mid = (high + low)/2; 
	}

  int ret = high > (numEntries - 1) ? (numEntries - 1) : high;

  pModule->increment(ret - last_pos);
	last_pos = ret;

  return ret;
}



void CBottomLeft::OnItemChanged(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
	int item = pNMLV->iItem; // item is the zero based index	

	ShowText(item);

	*pResult = 0;
}

void CBottomLeft::ShowText(int sel)
{
  if(!pModule) return;

	pModule->increment(sel - last_pos);
  last_pos = sel;

	CString chapText = font.GetHeaderRTF();

	CString titleSize;
	titleSize.Format(_T("\\cf2\\fs%d "), font.text_facesize + 5);
	chapText = chapText +  titleSize + (CString) pModule->getKeyText();
		
	// reset the font size
	titleSize.Format(_T("\\fs%d"), font.text_facesize);
	chapText = chapText + titleSize + _T("\\par\\par ");
							
	chapText = chapText +  (CString) pModule->RenderText() + _T("}"); 
							
	chapText.Replace(_T("<a href=\"\">"),  _T("\\cf6\\b "));
	chapText.Replace(_T("</a>"), _T("\\cf2\\b0 "));

	m_lxRich.SetRTF(chapText);

	ZoomText();



}

void CBottomLeft::OnShowVersions()
{
	RECT rc;
    m_wndFormatBar.GetToolBarCtrl().GetItemRect(0,&rc);
	m_wndFormatBar.ClientToScreen(&rc); // TrackPopupMenu uses screen coords

   CMenu menu;
   menu.CreatePopupMenu();
   CMenu* pPopup = NULL;
   pPopup = &menu;

   if(pPopup == NULL) return;
   int dwSelectionMade;  

 
   // fill in the strings for different commentary versions
	for(int i = 0; i < CSwordBibleDoc::numDictAvailable; i++)
		if(CSwordBibleDoc::pRTFDictModule[i] != NULL)
		{
			         pPopup->AppendMenu(MF_STRING,ID_DICTIONARY_NAMES + i, (CString) CSwordBibleDoc::pRTFDictModule[i]->Description());
					 if(active == i) pPopup->CheckMenuItem(ID_DICTIONARY_NAMES + i,MF_CHECKED); 

		}


         dwSelectionMade =  pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON|TPM_RETURNCMD, 
         rc.left, rc.bottom + 4, this);

	
		int selection = dwSelectionMade;
		int base = ID_DICTIONARY_NAMES;
		int temp = selection - base;


	if(temp >= 0 && temp < CSwordBibleDoc::numDictAvailable) 
	{

  active = temp;
	pModule = (sword::SWLD * )CSwordBibleDoc::pRTFDictModule[active];

  numEntries = GetNumEntries();
	EntriesList.SetItemCount(numEntries);

  last_pos = 0;
  pModule->setPosition(sword::TOP);
  pModule->increment(1);

 SearchEdit.SetWindowText(_T("Type text & hit Enter to search"));
 
   EntriesList.SetItemState(1,LVIS_SELECTED | LVIS_FOCUSED,LVIS_SELECTED | LVIS_FOCUSED);
   EntriesList.SetItemState(0,LVIS_SELECTED | LVIS_FOCUSED,LVIS_SELECTED | LVIS_FOCUSED);
	
	}

    pPopup->DestroyMenu(); 


}

void CBottomLeft::OnSearchDict()
{
	SearchEdit.UpdateData(TRUE);
  SearchEdit.GetWindowText(search);

	if(search.GetLength() < 1) return;
	
	FoundIndex = FindEntry(search);

  ASSERT(!pModule->Error());
  ASSERT(FoundIndex < numEntries);
  ASSERT(FoundIndex >= 0);
		 
	EntriesList.SetItemState(FoundIndex,LVIS_SELECTED | LVIS_FOCUSED,LVIS_SELECTED | LVIS_FOCUSED);
	EntriesList.EnsureVisible(FoundIndex,FALSE); 
}


void CBottomLeft::OnEnMsgfilter(NMHDR *pNMHDR, LRESULT *pResult)
{
	MSGFILTER *pMsgFilter = reinterpret_cast<MSGFILTER *>(pNMHDR);
	
	if (pMsgFilter->msg == WM_RBUTTONDOWN)   OnRightButtonDown();
   
     *pResult = 0; 



}

void CBottomLeft::OnRightButtonDown()
{

	  CPoint point;                                            
     ::GetCursorPos(&point); //where is the mouse?
     CMenu menu; //lets display out context menu :) 
     DWORD dwSelectionMade;                                       
     VERIFY(menu.LoadMenu(IDR_MENU_BOTTOMLEFT) );  
     CMenu *pmenuPopup = menu.GetSubMenu(0);
     ASSERT(pmenuPopup != NULL);   

	 if(CSwordBibleDoc::dictZoom)	pmenuPopup->CheckMenuItem(ID_DICTIONARY_ZOOMTEXT,MF_CHECKED); 

     dwSelectionMade = pmenuPopup->TrackPopupMenu( (TPM_LEFTALIGN|TPM_LEFTBUTTON|
                                                      TPM_RETURNCMD),
                                                    point.x, point.y, this); 

	

	
	switch (dwSelectionMade)
	{
	case ID_DICTIONARY_PRINT:		   OnPrint(); break;
	case ID_DICTIONARY_COPY:			   OnBottomleftCopy(); break;
	case ID_DICTIONARY_SELECTALL:       OnSelectAll(); break;
	case ID_DICTIONARY_DISPLAY:		   OnBottomleftFont(); break;    
	case ID_DICTIONARY_ZOOMTEXT:        OnZoom(); break; 
  case ID_DICTIONARY_PREVIOUSENTRY: OnPrevious(); break;
	case ID_DICTIONARY_NEXTENTRY:     OnNext(); break;
  case ID_DICTIONARY_INFORMATION:     OnInfo(); break;

	}

	pmenuPopup->DestroyMenu(); 



}


void CBottomLeft::OnFocusSearchEditBox()
{
  SearchEdit.SetWindowText(_T(""));
}

BOOL CBottomLeft::PreTranslateMessage(MSG* pMsg)
{
  if ( pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN ) {

  int idCtrl= this->GetFocus()->GetDlgCtrlID();

  if ( idCtrl == IDC_SEARCH_DICT )
  {
    OnSearchDict();
  }
}


  return CPropertyPage::PreTranslateMessage(pMsg);
}
